Conversation
…r composite cartel detection) Simple label-propagation community detector. Each node starts in its own community; each iteration, every node adopts the label with greatest weighted neighbor-vote (ties broken by lowest community id for determinism). Stops when no label changes or maxIterations reached. Surface: Graph.labelPropagation : int -> Graph<'N> -> Map<'N, int> Trade-offs (documented in XML-doc): - Fast: O(iterations × edges), no dense-matrix. - Quality: below Louvain on complex structures; catches obvious dense cliques reliably (exactly the trivial-cartel-detect case). - Determinism: tie-break by lowest id. - NOT a replacement for Louvain; dependency-free first pass. Composes with modularityScore (PR #324): LP produces partition, modularity evaluates it. Full end-to-end pattern verified in test labelPropagation produces partition consumable by modularityScore — two K3 cliques bridged thin → Q > 0.3. Tests (3 new, 31 total in GraphTests, all passing): - Empty graph -> empty map - Two K3 cliques converge to two labels (nodes within a clique share label) - LP partition consumed by modularityScore yields Q > 0.3 (cartel-detection pipeline correctness) Amara Otto-132 17th-ferry observation: her proposed 'CoordinationRiskScore' combines λ₁ + ΔQ + covariance + sync + exclusivity + influence. This graduation ships the ΔQ prerequisite (partition from LP + Q from modularityScore). Remaining primitives queued for future graduations per Otto-105 cadence. Build: 0 Warning / 0 Error. Provenance: - 12th ferry §5 + 13th ferry §2 + 14th ferry alert row - Implementation: Otto (13th graduation) Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
|
You have reached your Codex usage limits for code reviews. You can see your limits in the Codex usage dashboard. |
There was a problem hiding this comment.
Pull request overview
Adds a simple label-propagation community detector to Zeta.Core.Graph and verifies it composes with Graph.modularityScore for a toy “two cliques + bridge” scenario.
Changes:
- Introduce
Graph.labelPropagation : int -> Graph<'N> -> Map<'N,int>for deterministic label-propagation community detection. - Add xUnit/FsUnit tests covering empty-graph behavior, clique separation, and modularity scoring of the produced partition.
Reviewed changes
Copilot reviewed 2 out of 2 changed files in this pull request and generated 3 comments.
| File | Description |
|---|---|
| src/Core/Graph.fs | Adds Graph.labelPropagation implementation and API documentation. |
| tests/Tests.FSharp/Algebra/Graph.Tests.fs | Adds tests validating label propagation behavior and its composition with modularity scoring. |
| let (s, t) = entry.Key | ||
| let si = idx.[s] | ||
| let ti = idx.[t] | ||
| if entry.Weight <> 0L && si <> ti then |
There was a problem hiding this comment.
P0: Graph<'N> supports signed edge weights (retraction-native), but labelPropagation currently adds neighbors even when entry.Weight is negative. Because negative weights are later treated as 0 votes, nodes connected only via negative edges can still change label arbitrarily (all votes tie at 0, then tie-break picks lowest label). Consider filtering neighbors to entry.Weight > 0L (or otherwise handling signed weights explicitly) so anti-edges don’t influence community assignment unexpectedly.
| if entry.Weight <> 0L && si <> ti then | |
| if entry.Weight > 0L && si <> ti then |
| /// Provenance: 12th ferry §5 + 13th ferry §2 "community | ||
| /// detection" + 14th ferry alert row "Modularity Q jump > | ||
| /// 0.1 or Q > 0.4 (community-detection-based)". |
There was a problem hiding this comment.
The doc comment’s provenance references (“12th ferry §5”, “13th ferry §2”, “14th ferry alert row …”) don’t appear to resolve to any docs in-tree (searching docs/**/*.md only finds generic ferry mentions in the ADR, not these sections). Please either link to a concrete file path/anchor that exists in the repo, or remove/adjust the references so readers can actually follow them.
| /// Provenance: 12th ferry §5 + 13th ferry §2 "community | |
| /// detection" + 14th ferry alert row "Modularity Q jump > | |
| /// 0.1 or Q > 0.4 (community-detection-based)". | |
| /// Intended as a simple first-pass community-detection | |
| /// primitive for obvious dense-clique cases and modularity- | |
| /// oriented alerting heuristics. |
| let labelA = partition.[1] | ||
| let labelB = partition.[4] | ||
| // Both cliques share a label within themselves | ||
| partition.[2] |> should equal labelA | ||
| partition.[3] |> should equal labelA | ||
| partition.[5] |> should equal labelB | ||
| partition.[6] |> should equal labelB |
There was a problem hiding this comment.
This test claims “two labels” but never asserts that labelA and labelB differ. As written, it would still pass if label propagation collapses the whole graph into a single community. Add an explicit assertion that labelA <> labelB (or equivalent) to ensure the intended behavior is actually verified.
… detector) (#328) First full integration of the Graph detection pipeline: combines largestEigenvalue (spectral growth) + labelPropagation (community partition) + modularityScore (partition evaluation) into a single scalar risk score. Surface: Graph.coordinationRiskScore (alpha: double) (beta: double) (eigenTol: double) (eigenIter: int) (lpIter: int) (baseline: Graph<'N>) (attacked: Graph<'N>) : double option Composite formula (MVP): risk = alpha * Δλ₁_rel + beta * ΔQ where: - Δλ₁_rel = (λ₁(attacked) - λ₁(baseline)) / max(λ₁(baseline), eps) - ΔQ = Q(attacked, LP(attacked)) - Q(baseline, LP(baseline)) Both signals fire when a dense subgraph is injected: λ₁ grows because the cartel adjacency has high leading eigenvalue; Q grows because LP finds the cartel as its own community and Newman Q evaluates that partition highly. Weight defaults per Amara 17th-ferry initial priors: - alpha = 0.5 spectral growth - beta = 0.5 modularity shift Tests (3 new, 34 total in GraphTests, all passing): - Empty graphs -> None - Cartel injection -> composite > 1.0 (both signals fire) - attacked == baseline -> composite near 0 (|score| < 0.2) Calibration deferred (Amara Otto-132 Part 2 correction #4 — robust statistics via median + MAD): this MVP uses raw linear weighting over differences. Full CoordinationRiskScore with robust z-scores over baseline null-distribution is a future graduation once baseline-calibration machinery ships. RobustStats.robustAggregate (PR #295) already provides the median-MAD machinery; just needs a calibration harness to use it. 14th graduation under Otto-105 cadence. First full integration ship using 4 Graph primitives composed together (λ₁ + LP + modularity + composer). Build: 0 Warning / 0 Error. Provenance: - Concept: Aaron (firefly network + trivial-cartel-detect) + Amara's composite-score formulations across 12th/13th/14th/ 17th ferries - Implementation: Otto (14th graduation) Composes with: - Graph.largestEigenvalue (PR #321) - Graph.labelPropagation (PR #326) - Graph.modularityScore (PR #324) - RobustStats.robustAggregate (PR #295) — for future robust variant Co-authored-by: Claude Opus 4.7 <noreply@anthropic.com>
…ns tracked; 3 already shipped) (#330) * ferry: Amara 17th absorb — Cartel-Lab Implementation Closure + 5.5 Verification (8 corrections tracked) Two-part ferry: Amara's deep-research Implementation Closure for Cartel-Lab + her own GPT-5.5 Thinking verification pass with 8 load-bearing corrections. Otto correction-pass status (all 8 tracked): 1. λ₁(K₃) = 2 — ALREADY CORRECT PR #321 Otto-127 (independent convergence before verification arrived) 2. Modularity relational-not-absolute — ALREADY CORRECT PR #324 Otto-128 (caught mid-tick via hand-calc) 3. Cohesion/Exclusivity/Conductance replace entropy-collapse — SHIPPED PR #329 Otto-135 (3 primitives + 6 tests) 4. Windowed stake covariance acceleration — FUTURE GRADUATION 5. Event-stream → phase pipeline for PLV — FUTURE GRADUATION 6. 'ZSet invertible' → 'deltas support retractions' — ADR ALREADY PHRASED CORRECTLY (PR #316 never claimed full invertibility) 7. KSK 'contract' → 'policy layer' — FILED BACKLOG PR #318 Otto-124 (Max coord pending) 8. SOTA humility — DOC PHRASING (applied in new absorb docs) Amara's proposed 3-PR split NOT adopted (Otto-105 small- graduation cadence; content delivered across 7 ticks instead: PRs #317, #321, #323, #324, #326, #328, #329). Amara's proposed /cartel-lab/ folder NOT adopted (Otto-108 Conway's-Law: single-module-tree until interfaces harden). Current Graph.fs + test-support split works. Aaron's SharderInfoTheoreticTests flake flag (trailing Otto-132 note) filed as BACKLOG PR #327 Otto-133 — unrelated hygiene item. Amara's Otto-136 follow-up note: '#323 conceptually accepted, do not canonicalize until sharder test is seed-locked/ recalibrated'. Acknowledged — #323 lives in tests/Simulation/ already (test-scoped); 'canonicalize' = future promotion to src/Core/NetworkIntegrity/ per Amara's PR #3 split suggestion; that's gated on #327 completion. §33 archive header compliance. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com> * lint: fix line-start PR-number header false-positive in 17th-ferry absorb --------- Co-authored-by: Claude Opus 4.7 <noreply@anthropic.com>
…tion (Amara #3 correction) Applies Amara 17th-ferry Part 2 correction #3: replace muddy 'subgraph entropy collapse' with explicit cohesion / exclusivity / conductance metrics used in the cartel-detection literature (Wachs & Kertész 2019). Surface (3 new primitives): - Graph.internalDensity : Set<'N> -> Graph<'N> -> double option Internal edge weight / ordered-pair count. High value = tight sub-cluster. - Graph.exclusivity : Set<'N> -> Graph<'N> -> double option Internal weight / total-outgoing weight. Near 1 = cartel isolated; near its relative share = integrated community. - Graph.conductance : Set<'N> -> Graph<'N> -> double option Classical cut-to-volume ratio. Low = tight isolation. All three return None on degenerate inputs (size < 2 for density; empty set for exclusivity; empty/full for conductance). Tests (6 new, 37 total in GraphTests, all passing): - internalDensity None on |S| < 2 - internalDensity of K3 clique ≈ 10 (weight 10 per pair; ordered-pair count 6; density = 60/6) - exclusivity = 1 for isolated K3 - exclusivity < 1 but > 0.9 for K3 + 1 external edge - conductance < 0.1 for well-isolated K3 subset (bridged by thin edge to another K3) - conductance None on empty or full-graph subset Why this set: Amara's verification found 'subgraph entropy collapse' as stated was mathematically muddy — uniform dense clique has HIGH entropy over internal edges if weights are equal. Cohesion (internalDensity) + exclusivity + conductance capture cluster-like structure directly + are standard in the economic/sociological cartel-detection literature. Entropy can remain as secondary descriptor but these three are the primary group-level features. 15th graduation under Otto-105 cadence. Applies 1 of 5 future- graduation items from Amara 17th-ferry verification pass. Next graduation queue (remaining from Amara Otto-132 corrections): - Windowed stake covariance acceleration (#4) - Event-stream → phase pipeline for PLV (#5) - Robust-z-score variant of coordinationRiskScore (#4 robust statistics) Build: 0 Warning / 0 Error. Provenance: - Concept: Aaron (trivial cartel detect — first-order-signal tier) - Formalization: Wachs & Kertész 2019 (co-bidding network cohesion/exclusivity) via Amara's 14th + 17th ferries - Implementation: Otto (15th graduation) Composes with: - Graph.labelPropagation (PR #326) for community → subset input - RobustStats.robustAggregate (PR #295) for aggregating density/exclusivity/conductance across many candidate subsets outlier-resistantly Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
…tion (Amara #3 correction) Applies Amara 17th-ferry Part 2 correction #3: replace muddy 'subgraph entropy collapse' with explicit cohesion / exclusivity / conductance metrics used in the cartel-detection literature (Wachs & Kertész 2019). Surface (3 new primitives): - Graph.internalDensity : Set<'N> -> Graph<'N> -> double option Internal edge weight / ordered-pair count. High value = tight sub-cluster. - Graph.exclusivity : Set<'N> -> Graph<'N> -> double option Internal weight / total-outgoing weight. Near 1 = cartel isolated; near its relative share = integrated community. - Graph.conductance : Set<'N> -> Graph<'N> -> double option Classical cut-to-volume ratio. Low = tight isolation. All three return None on degenerate inputs (size < 2 for density; empty set for exclusivity; empty/full for conductance). Tests (6 new, 37 total in GraphTests, all passing): - internalDensity None on |S| < 2 - internalDensity of K3 clique ≈ 10 (weight 10 per pair; ordered-pair count 6; density = 60/6) - exclusivity = 1 for isolated K3 - exclusivity < 1 but > 0.9 for K3 + 1 external edge - conductance < 0.1 for well-isolated K3 subset (bridged by thin edge to another K3) - conductance None on empty or full-graph subset Why this set: Amara's verification found 'subgraph entropy collapse' as stated was mathematically muddy — uniform dense clique has HIGH entropy over internal edges if weights are equal. Cohesion (internalDensity) + exclusivity + conductance capture cluster-like structure directly + are standard in the economic/sociological cartel-detection literature. Entropy can remain as secondary descriptor but these three are the primary group-level features. 15th graduation under Otto-105 cadence. Applies 1 of 5 future- graduation items from Amara 17th-ferry verification pass. Next graduation queue (remaining from Amara Otto-132 corrections): - Windowed stake covariance acceleration (#4) - Event-stream → phase pipeline for PLV (#5) - Robust-z-score variant of coordinationRiskScore (#4 robust statistics) Build: 0 Warning / 0 Error. Provenance: - Concept: Aaron (trivial cartel detect — first-order-signal tier) - Formalization: Wachs & Kertész 2019 (co-bidding network cohesion/exclusivity) via Amara's 14th + 17th ferries - Implementation: Otto (15th graduation) Composes with: - Graph.labelPropagation (PR #326) for community → subset input - RobustStats.robustAggregate (PR #295) for aggregating density/exclusivity/conductance across many candidate subsets outlier-resistantly Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
…Corrections
Two-part ferry from Aaron Otto-157/158 tick boundary:
Part 1 — Deep research on Cartel-Lab calibration + CI hardening
(~4000 words; 8 sections A-H + action items + Mermaid diagrams):
- Null-models table (6 types: Erdős-Rényi, configuration,
stake-shuffle, temporal-shuffle, clustered-honest, noise)
- CoordinationRiskScore formula with 6 robust-z terms +
default weights α=β=0.20, γ=ε=0.15, δ=0.20, η=0.10
- 8-row adversarial scenario table (obvious clique → stealth
→ synchronized voting → honest cluster → low-weight →
camouflage → rotating → cross-coalition)
- 4-PR roadmap: seed-lock/CI governance → calibration harness
→ adversarial scenarios → docs/promotion criteria
- KSK/Aurora integration: advisory-only flow
(Detection → Oracle → KSK → Action)
- "What not to claim" caveats (6 items: no proof of intent,
not all collusion detectable, not production-ready, etc.)
Part 2 — Amara's own GPT-5.5 Thinking correction pass on Part 1
(~1500 words; 10 required corrections; repo-safe status
statement; corrected promotion ladder + PR roadmap titles):
- #1: replace "CI confirms" with "PR #323 clears toy
falsifiability bar"
- #2: Wilson intervals replace handwave ±5% CI (90/100 →
LB only 82.6%; 20/100 FPR → UB 28.9%)
- #3: rename "Cartel Score" → "CoordinationRiskScore" locked
- #4: conductance sign flip — use Z(-conductance) or
Z(exclusivity), not Z(+conductance)
- #5: modularity relational — use Q(attacked)-Q(baseline)>θ
not absolute Q thresholds
- #6: PLV phase-offset — PLV=1 can mean anti-phase; need
magnitude AND mean phase offset
- #7: MAD=0 fallback — epsilon floor or percentile-rank
- #8: replace Medium-article source with scikit-learn
precision-recall docs
- #9: explicit artifact output layout
(calibration-summary.json, seed-results.csv, etc.)
- #10: sharder — measure variance before widening threshold
Corrected promotion ladder (0-6 stages):
0 Theory / 1 Toy detector / 2 Calibration harness /
3 Scenario suite / 4 Advisory engine / 5 Governance integration /
6 Enforcement candidate
PR #323 is Stage 1, NOT Stage 4.
Otto's operationalization notes:
- 4/10 corrections already aligned with shipped substrate:
#4 exclusivity (PR #331), #5 modularity relational
(PR #324), #7 MAD floor (PR #333), #10 sharder Otto-132
(BACKLOG #327).
- 6/10 queued as future graduations: Wilson CIs in tests;
MAD=0 percentile-rank fallback; conductance-sign doc;
PLV phase-offset extension; CI test classification;
artifact-output layout.
Invariant restated (Amara 16th-ferry carry-over):
"Every abstraction must map to a repo surface, a test,
a metric, or a governance rule."
Cross-ref verified: PRs #321 #323 #324 #326 #327 #331 #332
#333, docs/definitions/KSK.md (Otto-157 / #336), 17th ferry
(#330), 16th ferry, 15th ferry, Otto-140..145 memory.
GOVERNANCE §33 four-field header (Scope / Attribution /
Operational status / Non-fusion disclaimer).
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
…ns (10 tracked; 4 already shipped, 6 queued) (#337) * ferry: Amara 18th absorb — Calibration + CI Hardening + 5.5-Thinking Corrections Two-part ferry from Aaron Otto-157/158 tick boundary: Part 1 — Deep research on Cartel-Lab calibration + CI hardening (~4000 words; 8 sections A-H + action items + Mermaid diagrams): - Null-models table (6 types: Erdős-Rényi, configuration, stake-shuffle, temporal-shuffle, clustered-honest, noise) - CoordinationRiskScore formula with 6 robust-z terms + default weights α=β=0.20, γ=ε=0.15, δ=0.20, η=0.10 - 8-row adversarial scenario table (obvious clique → stealth → synchronized voting → honest cluster → low-weight → camouflage → rotating → cross-coalition) - 4-PR roadmap: seed-lock/CI governance → calibration harness → adversarial scenarios → docs/promotion criteria - KSK/Aurora integration: advisory-only flow (Detection → Oracle → KSK → Action) - "What not to claim" caveats (6 items: no proof of intent, not all collusion detectable, not production-ready, etc.) Part 2 — Amara's own GPT-5.5 Thinking correction pass on Part 1 (~1500 words; 10 required corrections; repo-safe status statement; corrected promotion ladder + PR roadmap titles): - #1: replace "CI confirms" with "PR #323 clears toy falsifiability bar" - #2: Wilson intervals replace handwave ±5% CI (90/100 → LB only 82.6%; 20/100 FPR → UB 28.9%) - #3: rename "Cartel Score" → "CoordinationRiskScore" locked - #4: conductance sign flip — use Z(-conductance) or Z(exclusivity), not Z(+conductance) - #5: modularity relational — use Q(attacked)-Q(baseline)>θ not absolute Q thresholds - #6: PLV phase-offset — PLV=1 can mean anti-phase; need magnitude AND mean phase offset - #7: MAD=0 fallback — epsilon floor or percentile-rank - #8: replace Medium-article source with scikit-learn precision-recall docs - #9: explicit artifact output layout (calibration-summary.json, seed-results.csv, etc.) - #10: sharder — measure variance before widening threshold Corrected promotion ladder (0-6 stages): 0 Theory / 1 Toy detector / 2 Calibration harness / 3 Scenario suite / 4 Advisory engine / 5 Governance integration / 6 Enforcement candidate PR #323 is Stage 1, NOT Stage 4. Otto's operationalization notes: - 4/10 corrections already aligned with shipped substrate: #4 exclusivity (PR #331), #5 modularity relational (PR #324), #7 MAD floor (PR #333), #10 sharder Otto-132 (BACKLOG #327). - 6/10 queued as future graduations: Wilson CIs in tests; MAD=0 percentile-rank fallback; conductance-sign doc; PLV phase-offset extension; CI test classification; artifact-output layout. Invariant restated (Amara 16th-ferry carry-over): "Every abstraction must map to a repo surface, a test, a metric, or a governance rule." Cross-ref verified: PRs #321 #323 #324 #326 #327 #331 #332 #333, docs/definitions/KSK.md (Otto-157 / #336), 17th ferry (#330), 16th ferry, 15th ferry, Otto-140..145 memory. GOVERNANCE §33 four-field header (Scope / Attribution / Operational status / Non-fusion disclaimer). Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com> * ferry: fix markdownlint MD018 — line-start #221 parsed as H1 heading * ferry: drain PR #337 review threads — 4 FIX, 2 NARROW+BACKLOG, 8 BACKLOG+RESOLVE Factory-authored sections of the 18th-ferry absorb (header, Otto's notes, Cross-references) edited under name-attribution + code-comments-not-history disciplines; Amara's verbatim Part 1 + Part 2 body left intact per verbatim-preserve. In-doc edits: - Soften "verified against actual" wording on the CLAUDE.md cross-reference bullet to anchor-list rechecked-at-drain-time framing. - Use full `tests/Tests.FSharp/Simulation/` path in the Stage-discipline section (was bare `tests/Simulation/`). - Replace dead "GOVERNANCE §33" cite with factory-convention + CLAUDE.md ground-rule pointer (numbered §33 not yet landed; rule is captured by convention across docs/aurora/** absorbs). - Drop broken `feedback_ksk_naming_*.md` filename and soften 15th/16th ferry cross-refs to "not present as a dedicated absorb in this snapshot." Drain-log: docs/pr-preservation/337-drain-log.md per Otto-250. --------- Co-authored-by: Claude Opus 4.7 <noreply@anthropic.com>
Simple label-propagation community detector. Composes with modularityScore: LP produces partition, Q evaluates it. End-to-end test: two K3 cliques bridged thin → Q > 0.3. 31 GraphTests passing.